/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sinai.mshab.client.util.Util;
import com.sinai.mshab.client.ui.Key;
import com.sinai.mshab.client.ui.aggregate.FormData;
import com.sinai.mshab.server.sql.SQLSelect;

public class FileDownloadServlet extends HttpServlet {

	private static final int FILE_READ_BUFFER_LIMIT = 256;

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		doPost(request, response);
	}

	/**
	 * We expect the following parameters: the key, fileNameFieldName,
	 * fileNameTableName, fileFieldName, fileTableName.
	 */
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {

		// get the required DB information.
		String fileNameFieldName = (String) request
				.getParameter("fileNameFieldName");
		String fileNameTableName = (String) request
				.getParameter("fileNameTableName");
		String fileFieldName = (String) request.getParameter("fileFieldName");
		String fileTableName = (String) request.getParameter("fileTableName");

		Key dbKeys = parseKeys(request, fileTableName);

		// tell the server to put the file on the server's hard drive.
		String pathToFile = null;
		try {
			pathToFile = getFile(fileNameFieldName, fileNameTableName,
					fileFieldName, fileTableName, dbKeys);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (pathToFile != null) {

			// set the file's mimetype.
			// response.setContentType("application/pdf");
			response.setHeader("Content-Disposition", "inline; filename="
					+ Util.parseFileName(pathToFile));

			// open the file.
			File file = new File(pathToFile);

			// int counter = 0;
			// while (!file.exists()) {
			// counter++;
			// sleep(1000);
			// System.out.println("Waiting for file to be read for " + counter
			// + " seconds");
			// }
			FileInputStream fileStream = new FileInputStream(file);

			// read a certain number of bytes and write it to the response.
			ServletOutputStream out = response.getOutputStream();
			byte[] fileChunk = new byte[FILE_READ_BUFFER_LIMIT];
			while (fileStream.read(fileChunk) > 0) {
				out.write(fileChunk);
			}

			// flush the output.
			out.flush();
			fileStream.close();
		} else {
			// TODO: should go to error log.

			System.out.println("Couldn't get the files name!!!");
		}
	}

	private void sleep(long millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Returns the path to where the file, retrieved from fileFieldName and
	 * fileFieldTable, was downloaded to on the file system.
	 * 
	 * @param fileNameFieldName
	 *            the field name holding the name of the file.
	 * @param fileNameTableName
	 *            the table name where the fileNameFieldName is stored.
	 * @param fileFieldName
	 *            the field name holding the binary file.
	 * @param fileTableName
	 *            the tabel name where fileFieldName is stored.
	 * @param dbKeys
	 *            the Key representing which record we want.
	 * @return the path to where the file, retrieved from fileFieldName and
	 *         fileFieldTable, was downloaded to on the file system.
	 * @throws SQLException
	 * @throws IOException
	 */
	public String getFile(String fileNameFieldName, String fileNameTableName,
			String fileFieldName, String fileTableName, Key dbKeys)
			throws SQLException, IOException {

		// get the connection.
		Connection conn = Server.getConn();

		// start constructing the SQL for getting the binary file.
		SQLSelect retrieveFileSQL = new SQLSelect(conn);
		retrieveFileSQL.addSelectField(fileTableName, fileFieldName);

		// start constructing the SQL for getting the file's name.
		SQLSelect retrieveFileNameSQL = new SQLSelect(conn);
		retrieveFileNameSQL
				.addSelectField(fileNameTableName, fileNameFieldName);
		String result = null;

		// add the values of dbKeys to our two SELECT queries.
		retrieveFileSQL.addWhere(dbKeys);
		retrieveFileNameSQL.addWhere(dbKeys);

		ResultSet file = retrieveFileSQL.execute();
		ResultSet fileName = retrieveFileNameSQL.execute();

		if (file.first() && fileName.first()) {
			byte[] blob = file.getBytes(fileFieldName);

			// use the /tmp directory and create a unique directory to avoid
			// overwrites when concurrently used.
			String path = "/tmp/" + (new Date()).getTime() + "/";
			boolean directory = new File(path).mkdirs();
			if (directory) {

				// save the file path and return it.
				result = path
						+ Util.parseFileName(fileName
								.getString(fileNameFieldName));

				// save the file on the file system.
				File ioFile = new File(result);
				FileOutputStream fileStream = new FileOutputStream(ioFile);
				fileStream.write(blob);
			} else {
				// TODO: add to log.
				System.out.println("Failed to create directory " + path);
			}
		}

		retrieveFileSQL.close();
		retrieveFileNameSQL.close();
		
		return result;
	}

	/**
	 * Creates a {@link Key} by extracting the "cond" parameters from request.
	 * 
	 * @param request
	 *            the {@link HttpServletRequest} that contains the condition
	 *            parameters that should make up the {@link Key}
	 * @param tableName
	 *            the table name where the key lives.
	 * @return a {@link Key} by extracting the "cond" parameters from request.
	 */
	private Key parseKeys(HttpServletRequest request, String tableName) {

		Key result = new Key();

		// get the parameters in request and go through them.
		Enumeration enumerator = request.getParameterNames();
		while (enumerator.hasMoreElements()) {
			String param = (String) enumerator.nextElement();

			// if the parameter is a condition parameter ("cond")...
			if (param.toString().startsWith("cond")) {

				// then we make a key out of it.
				String key = param.substring("cond".length());
				String value = request.getParameter(param);
				result.add(key, value, tableName);
			}
		}

		return result;
	}
}
